package com.android.avy;

import java.util.HashMap;

import android.graphics.Path;
import android.graphics.RectF;
import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.PathShape;

public class DangerRose {

	static final int N = 0;
	static final int NE = 1;
	static final int E = 2;
	static final int SE = 3;
	static final int S = 4;
	static final int SW = 5;
	static final int W = 6;
	static final int NW = 7;
	static final int NUM_BEARINGS = 8;
	static final int BELOW = 0;
	static final int TREELINE = 1;
	static final int ABOVE = 2;
	static final int NUM_LEVELS = 3;
	static final int NUM_PETALS = 24;

	// size in pixels of the danger rose drawable (the actual size
	// will depend on the View container methinks
	static final float SIZE = 100;
	static final float HALF_SIZE = 50;
	// width in pixels of the spaces between rose petals
	static final float SPOKE = 4;
	// radial width of the outer rose petals, in pixels
	// (HALF_SIZE / (NUM_LEVELS + some int) so that inner
	// rose petals have have more room
	static final float RING_WIDTH = 50 / 4;
	// inner radius of innermost rose petals (determined by SPOKE)
	static final float RMIN = 0.5f * 4 / ((float) Math
			.sin(Math.toRadians(22.5)));

	static final int BLANK = 0xFFA1A1A1;
	static final int GRAY = 0xFFA1A1A1;
	static final int WHITE = 0xFFFFFFFF;
	static final int RED = 0xFFFF0000;
	static final int GREEN = 0xFF00FF00;
	static final int YELLOW = 0xFFFFFF00;
	static final int ORANGE = 0xFFFF7F00;
	
	static final HashMap<Integer, Integer> colormap;
	static {
		colormap = new HashMap<Integer, Integer>();
		colormap.put(0, BLANK);
		colormap.put(1, GREEN);
		colormap.put(3, YELLOW);
		colormap.put(5, ORANGE);
		colormap.put(7, RED);
		colormap.put(9, WHITE);
	}
	
	static final String WAIT_ROSE_FRAME1 = "999000000000000000000000";
	static final String WAIT_ROSE_FRAME2 = "000999000000000000000000";
	static final String WAIT_ROSE_FRAME3 = "000000999000000000000000";
	static final String WAIT_ROSE_FRAME4 = "000000000999000000000000";
	static final String WAIT_ROSE_FRAME5 = "000000000000999000000000";
	static final String WAIT_ROSE_FRAME6 = "000000000000000999000000";
	static final String WAIT_ROSE_FRAME7 = "000000000000000000999000";
	static final String WAIT_ROSE_FRAME8 = "000000000000000000000999";
	
	static final String[] WAIT_ROSE_FRAMES = {"999000000000000000000000",
		"000999000000000000000000",
		"000000999000000000000000",
		"000000000999000000000000",
		"000000000000999000000000",
		"000000000000000999000000",
		"000000000000000000999000",
		"000000000000000000000999"};

	static final AnimationDrawable waitRose = new AnimationDrawable();
	static {
		waitRose.addFrame(buildRose(WAIT_ROSE_FRAME1), 100);
		waitRose.addFrame(buildRose(WAIT_ROSE_FRAME2), 100);
		waitRose.addFrame(buildRose(WAIT_ROSE_FRAME3), 100);
		waitRose.addFrame(buildRose(WAIT_ROSE_FRAME4), 100);
		waitRose.addFrame(buildRose(WAIT_ROSE_FRAME5), 100);
		waitRose.addFrame(buildRose(WAIT_ROSE_FRAME6), 100);
		waitRose.addFrame(buildRose(WAIT_ROSE_FRAME7), 100);
		waitRose.addFrame(buildRose(WAIT_ROSE_FRAME8), 100);
		waitRose.setOneShot(false);
	}

	/** 
	 * Cannot be instantiated.
	 */
	private DangerRose() {}

	/**
	 * Builds and returns a drawable for a single petal of a Danger Rose.
	 * 
	 * @param color A color (integer) with which to fill the rose petal.
	 * @param bearing Slope bearing of rose petal (DangerRose.NW, DangerRose.E, etc.)
	 * @param level Altitude of rose petal (DangerRose.TREELINE, etc.)
	 * 
	 * @return A Drawable for the single rose petal
	 */
	static public Drawable drawPetal(int color, int bearing, int level) {
		// inner radius of petal
		float r1;
		// if elevation of petal is top-most, the inner radius is the minimum
		// radius
		if (level == ABOVE) {
			r1 = RMIN;
		} else {
			r1 = HALF_SIZE - RING_WIDTH * (level + 1) + 0.5f * SPOKE;
		}
		float r2 = HALF_SIZE - RING_WIDTH * level - 0.5f * SPOKE;

		// theta11: inner radius, counter-clockwise corner of petal
		// theta12: inner radius, clockwise corner of petal
		// theta21: outer radius, counter-clockwise corner of petal
		// theta22: outer radius, clockwise corner of petal
		float theta11 = (((float) bearing) - 0.5f)
				* 45.0f
				+ 0.5f
				* ((float) Math.toDegrees(Math.acos(1 - 0.5f * SPOKE * SPOKE
						/ (r1 * r1)))) - 90;
		float theta12 = (((float) bearing) + 0.5f)
				* 45.0f
				- 0.5f
				* ((float) Math.toDegrees(Math.acos(1 - 0.5f * SPOKE * SPOKE
						/ (r1 * r1)))) - 90;
		float theta21 = (((float) bearing) - 0.5f)
				* 45.0f
				+ 0.5f
				* ((float) Math.toDegrees(Math.acos(1 - 0.5f * SPOKE * SPOKE
						/ (r2 * r2)))) - 90;
		float theta22 = (((float) bearing) + 0.5f)
				* 45.0f
				- 0.5f
				* ((float) Math.toDegrees(Math.acos(1 - 0.5f * SPOKE * SPOKE
						/ (r2 * r2)))) - 90;

		// rectangles bounding circles that describe inner and outer arcs of
		// rose petal
		RectF rect1 = new RectF(HALF_SIZE - r1, HALF_SIZE - r1, HALF_SIZE + r1,
				HALF_SIZE + r1);
		RectF rect2 = new RectF(HALF_SIZE - r2, HALF_SIZE - r2, HALF_SIZE + r2,
				HALF_SIZE + r2);

		// draw the outline of the rose petal in a clockwise fashion
		Path petalPath = new Path();
		petalPath.addArc(rect2, theta21, theta22 - theta21);
		petalPath.arcTo(rect1, theta12, theta11 - theta12);
		petalPath.close();

		// encapsulate outline in a ShapeDrawable so we can fill with color
		ShapeDrawable petalDraw = new ShapeDrawable(new PathShape(petalPath,
				SIZE, SIZE));
		petalDraw.getPaint().setColor(color);

		// set the intrinsic dimensions so that Views like ImageView know at
		// what size
		// to draw
		//petalDraw.setIntrinsicHeight((int) SIZE);
		//petalDraw.setIntrinsicWidth((int) SIZE);
		petalDraw.setIntrinsicHeight(4);
		petalDraw.setIntrinsicWidth(4);

		// return it!
		return petalDraw;

	}

	/**
	 * Builds the Danger Rose. (Should this be a private function?)
	 * 
	 * @param colors An array of color integers.
	 * @return The Danger Rose drawable.
	 */
	static public Drawable buildRose(int[] colors) {
		// if the input array is not long enough, fill the rest with the
		// default DangerRose.BLANK color
		int[] backupColors;
		if (colors.length < NUM_PETALS) {
			backupColors = new int[NUM_PETALS];
			for (int i = 0; i < NUM_PETALS; i++) {
				backupColors[i] = i < colors.length ? colors[i] : BLANK;
			}
		} else {
			backupColors = colors;
		}

		// draw each petal with the right color
		Drawable[] petals = new Drawable[NUM_LEVELS * NUM_BEARINGS];
		int i = 0;
		for (int bearing = 0; bearing < NUM_BEARINGS; bearing++) {
			for (int level = 0; level < NUM_LEVELS; level++) {
				petals[i] = drawPetal(backupColors[i], bearing, level);
				i++;
			}
		}
		return new LayerDrawable(petals);
	}

	/**
	 * Build the Danger Rose using a string of single-digit integers. Refer to
	 * the colormap static data member of this class for the mappings. If there are not
	 * enough characters, the string is padded (at the end) with "0"s, which show up
	 * gray on the Danger Rose.
	 * 
	 * @param strColors A string of single-digit integers. e.g. "111311533113111311533113"
	 * where the character-length of the string = NUM_PETALS = 24 (can be less or more,
	 * but the result will likely be unexpected, e.g. colors on the wrong rose petals)
	 * 
	 * @return The Danger Rose drawable.
	 */
	static public Drawable buildRose(String strColors) {
		int[] intColors = new int[NUM_PETALS];
		int strColor;

		String newStrColors = strColors;
		while (newStrColors.length() < NUM_PETALS) {
			newStrColors = newStrColors + "0";
		}

		for (int i = 0; i < NUM_PETALS; i++) {
			strColor = Integer.parseInt(newStrColors.substring(i, i + 1));
			if (colormap.containsKey(strColor)) {
				intColors[i] = colormap.get(strColor);
			} else {
				intColors[i] = BLANK;
			}
		}

		return buildRose(intColors);
	}
	
	static public AnimationDrawable getWaitRose() {
		return waitRose;
	}
	
	static public AnimationDrawable getNewWaitRose() {
		AnimationDrawable ad = new AnimationDrawable();
		for (String colors : WAIT_ROSE_FRAMES) {
			ad.addFrame(buildRose(colors), 200);
		}
		ad.setOneShot(false);
		return ad;
	}

}
